#include "undomanager.h"

#include <ieditor.h>
#include <ieditorarea.h>
#include <iperspective.h>

#include <QtGui/QUndoView>

using namespace core;
using namespace swp;

UndoManager* undoManager = 0;

UndoManager* UndoManager::getInstance()
{
    if (undoManager == 0)
    {
        undoManager = new UndoManager();
    }

    return undoManager;
}

void UndoManager::destroy()
{
    delete undoManager;
    undoManager = 0;
}

void UndoManager::bindActions(QAction* undoAction, QAction* redoAction,
    QAction* undoMenuAction, QAction* redoMenuAction)
{
    m_undoAction = undoAction;
    m_redoAction = redoAction;

    m_undoMenuAction = undoMenuAction;
    m_redoMenuAction = redoMenuAction;

    disableUndo();
    disableRedo();
}

void UndoManager::bindView(QUndoView* view)
{
    m_view = view;

    if (m_stacks.contains(m_currentDataContext) && m_view != 0)
    {
        m_view->setStack(m_stacks[m_currentDataContext]);
    }
}

UndoManager::UndoManager() : QObject(),
    m_undoAction(0),
    m_undoMenuAction(0),
    m_redoAction(0),
    m_redoMenuAction(0),
    m_view(0),
    m_currentPerspective(0),
    m_currentDataContext(0),
    m_currentContext(0),
    m_stacks()
{

}

UndoManager::~UndoManager()
{
}

void UndoManager::disableUndo()
{
    Q_ASSERT(m_undoAction != 0 && m_undoMenuAction != 0); // call bindActions first

    m_undoAction->setEnabled(false);
    m_undoMenuAction->setEnabled(false);
}

void UndoManager::disableRedo()
{
    Q_ASSERT(m_redoAction != 0 && m_redoMenuAction != 0); // call bindActions first

    m_redoAction->setEnabled(false);
    m_redoMenuAction->setEnabled(false);
}

void UndoManager::enableUndo()
{
    m_undoAction->setEnabled(true);
    m_undoMenuAction->setEnabled(true);
}

void UndoManager::enableRedo()
{
    m_redoAction->setEnabled(true);
    m_redoMenuAction->setEnabled(true);
}

void UndoManager::checkCapabilites(QUndoStack* stack)
{
    if (stack->canUndo())
    {
        enableUndo();
    }
    else
    {
        disableUndo();
    }
    if (stack->canRedo())
    {
        enableRedo();
    }
    else
    {
        disableRedo();
    }
}

void UndoManager::switchStack(QUndoStack* stack)
{
    if (m_view != 0)
    {
        m_view->setStack(stack);
    }
}

// public slots
void UndoManager::createStack(IDocumentData* data)
{
    if (!m_stacks.contains(data))
    {
        m_stacks[data] = new QUndoStack(this);
    }
}

void UndoManager::destroyStack(IDocumentData* data)
{
    if (m_stacks.contains(data))
    {
        delete m_stacks[data];
        m_stacks.remove(data);
    }
}

void UndoManager::clearStack(IDocumentData* data)
{
    if (m_stacks.contains(data))
    {
        m_stacks[data]->clear();

        checkCapabilites(m_stacks[data]);
    }
}

void UndoManager::push(IDocumentData* data,
    QUndoCommand* command)
{
    if (m_stacks.contains(data))
    {
       QUndoStack* stack = m_stacks[data];
       stack->push(command);

       checkCapabilites(stack);
    }
}

void UndoManager::undo()
{
    if (m_stacks.contains(m_currentDataContext))
    {
        QUndoStack* stack = m_stacks[m_currentDataContext];
        stack->undo();

        checkCapabilites(stack);

        if (stack->isClean())
        {
            emit currentStackCleared(m_currentContext);
        }
    }
}

void UndoManager::redo()
{
    if (m_stacks.contains(m_currentDataContext))
    {
        QUndoStack* stack = m_stacks[m_currentDataContext];
        stack->redo();

        checkCapabilites(stack);

        if (stack->isClean())
        {
            emit currentStackCleared(m_currentContext);
        }
    }
}

void UndoManager::handlePerspectiveChange(core::IPerspective* perspective)
{
    m_currentPerspective = perspective;

    if (!m_currentPerspective->getEditorArea()->currentEditor().isNull())
    {
        handleChangeOfDocument(m_currentPerspective,
            m_currentPerspective->getEditorArea()->currentEditor(
                )->document());
    }
}

void UndoManager::handleChangeOfDocument(IPerspective* perspective,
    const QSharedPointer<IDocument>& document)
{
    IDocumentData* data = document.isNull() ? 0 : document->data();
    if (m_currentPerspective == perspective)
    {
        if (data != 0 && m_stacks.contains(data))
        {
            m_currentDataContext = data;
            m_currentContext = document;

            QUndoStack* stack = m_stacks[data];

            checkCapabilites(stack);
            switchStack(stack);
        }
        else
        {
            disableUndo();
            disableRedo();
        }
    }
}

void UndoManager::handleDocumentSave(
    const QSharedPointer<core::IDocument>& document)
{
    IDocumentData* data = document.isNull() ? 0 : document->data();
    if (data != 0 && m_stacks.contains(data))
    {
        QUndoStack* stack = m_stacks[data];

        stack->setClean();
    }
}
